package SQL.scan;

import java.util.ArrayList;

import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory.Default;

import exceptions.DBTypeNotMatchException;
import heap.ResultField;
import heap.ResultRow;
import heap.ResultSchema;
import Common.Types;
import SQL.abysnSQL.ConstValue;
import SQL.abysnSQL.Func;
import SQL.abysnSQL.SelectExpList;
import SQL.abysnSQL.SelectExpr;
import SQL.abysnSQL.Value;
import SQL.abysnSQL.Func.FUNC;
import Transaction.Transaction;


public class ProjectScan implements Scan {
	public ProjectScan(Scan s,  String db,SelectExpList fieldList, Transaction tx) throws Exception {
		this.s = s;
		this.db = db;
		this.fieldList = fieldList;
		this.tx = tx;
		order = new ArrayList<Integer>();
		schema = new ResultSchema();
		tSchema = s.getSchema();
		SelectExpList fields = fieldList;
		if (fieldList.selExpr.type == SelectExpr.TYPE.ALL) {
			schema = tSchema;
		}
		else {
			while (fieldList != null) {
				if (fieldList.selExpr.value != null) {
					Value v = fieldList.selExpr.value;
					int value = v.getType(tSchema, db, tx);
					String name = v.toString();
					schema.add(new ResultField(name, value));
					for (int i = 0; i < tSchema.size(); i++) {
						if (tSchema.get(i).name.endsWith("."+name) ||
								tSchema.get(i).name.equals(name)) {
							order.add(i);
							break;
						}
					}
				}
				else {
					aggr = true;
					String name = fieldList.selExpr.colname.toString();
					ResultField rf = null;
					for (int i = 0; i < tSchema.size(); i++) {
						if (tSchema.get(i).name.endsWith("."+name) ||
								tSchema.get(i).name.equals(name)) {
							order.add(i);
							break;
						}
					}
					switch (fieldList.selExpr.func.value) {
					case AVG:
						rf = new ResultField("AVG("+name+")", Types.FLOAT);
						break;
					case COUNT:
						rf = new ResultField("SUM("+name+")", Types.INT);
						break;
					case SUM:
						rf = new ResultField("SUM("+name+")", 0);
						for (int i = 0; i < tSchema.size(); i++) {
							if (tSchema.get(i).name.endsWith("."+name) ||
									tSchema.get(i).name.equals(name)) {
								rf.type = tSchema.get(i).type;
								break;
							}
						}
						break;
					case MIN:
						rf = new ResultField("MIN("+name+")", 0);
						for (int i = 0; i < tSchema.size(); i++) {
							if (tSchema.get(i).name.endsWith("."+name) ||
									tSchema.get(i).name.equals(name)) {
								rf.type = tSchema.get(i).type;
								break;
							}
						}
						break;
					case MAX:
						rf = new ResultField("MAX("+name+")", 0);
						for (int i = 0; i < tSchema.size(); i++) {
							if (tSchema.get(i).name.endsWith("."+name) ||
									tSchema.get(i).name.equals(name)) {
								rf.type = tSchema.get(i).type;
								break;
							}
						}
						break;
					}
					schema.add(rf);
				}
				fieldList = fieldList.next;
			}
		}
		if (aggr) {
			ResultRow currentScanResult = s.next();
			ResultRow nextScanResult = currentScanResult;
			int count = 0;
			ArrayList<String> result = new ArrayList<String>();
			do {
				currentScanResult = nextScanResult;
				SelectExpList list = fields;
				int i = 0;
				count++;
				while (list != null) {
					if (list.selExpr.value != null) {
						if (result.size() == i) {
							result.add(currentScanResult.get(order.get(i)));
						}
					}
					else {
						switch(list.selExpr.func.value) {
						case COUNT:
							if (result.size() == i) {
								result.add("1");
							} else {
								int p = Integer.parseInt(result.get(i))+1;
								result.set(i, p+"");
							}
							break;
						case AVG:
							if (result.size() == i) {
								result.add(currentScanResult.get(order.get(i)));
							} else {
								float p = Float.parseFloat(result.get(i));
								p += Float.parseFloat(currentScanResult.get(order.get(i)));
								result.set(i, p+"");
							}
							break;
						case SUM:
							if (result.size() == i) {
								result.add(currentScanResult.get(order.get(i)));
							} else {
								float p = Float.parseFloat(result.get(i));
								p += Float.parseFloat(currentScanResult.get(order.get(i)));
								result.set(i, p+"");
							}
							break;
						case MAX:
							if (result.size() == i) {
								result.add(currentScanResult.get(order.get(i)));
							} else {
								switch(schema.get(i).type) {
								case Types.INT:
								case Types.BOOLEAN:
								case Types.FLOAT:
									float a = Float.parseFloat(result.get(i));
									float b = Float.parseFloat(currentScanResult.get(order.get(i)));
									if (a < b) {
										result.set(i, currentScanResult.get(order.get(i)));
									}
									break;
								default:
									String aa = result.get(i);
									String bb = currentScanResult.get(order.get(i));
									if (aa.compareTo(bb) < 0) {
										result.set(i, bb);
									}
								}
							}
							break;
						case MIN:
							if (result.size() == i) {
								result.add(currentScanResult.get(order.get(i)));
							} else {
								switch(schema.get(i).type) {
								case Types.INT:
								case Types.BOOLEAN:
								case Types.FLOAT:
									float a = Float.parseFloat(result.get(i));
									float b = Float.parseFloat(currentScanResult.get(order.get(i)));
									if (a > b) {
										result.set(i, currentScanResult.get(order.get(i)));
									}
									break;
								default:
									String aa = result.get(i);
									String bb = currentScanResult.get(order.get(i));
									if (aa.compareTo(bb) > 0) {
										result.set(i, bb);
									}
								}
							}
							break;
						}
					}
					list = list.next;
					i++;
				}
				nextScanResult = s.next();
			} while (nextScanResult != null);
			currentScanResult = nextScanResult;
			SelectExpList list = fields;
			int i = 0;
			currentResult = new ResultRow();
			while (list != null) {
				if (list.selExpr.value != null) {
					currentResult.add(result.get(i));
				} else {
					switch(list.selExpr.func.value) {
					case AVG:
						float p = Float.parseFloat(result.get(i));
						p = p / count;
						currentResult.add(p+"");
						break;
					case SUM:
						if(schema.get(i).type == Types.INT) {
							currentResult.add(result.get(i).split("\\.")[0]);
							break;
						}
					default:
						currentResult.add(result.get(i));
					}
				}
				list = list.next;
				i++;
			}
		}
	}

	public ResultRow next() throws Exception {
		if (aggr) {
			if (aggr_get)
				return null;
			aggr_get = true;
			return currentResult;
		}
		
		ResultRow res = s.next();
		if (res == null)
			return null;
		if (fieldList.selExpr.type == SelectExpr.TYPE.ALL) {
			currentResult = res;
			return res;
		}
		currentResult = new ResultRow();
		SelectExpList fieldList = this.fieldList;
		int i = 0;
		while (fieldList != null) {
			if (fieldList.selExpr.type == SelectExpr.TYPE.VALUE) {
				ConstValue v = fieldList.selExpr.value.getFinalValue(tSchema, res, db, tx);
				currentResult.add(v.toString());
			}
			fieldList = fieldList.next;
		}
		return currentResult;
	}

	public void beforeFirst() throws Exception {
		aggr_get = false;
		s.beforeFirst();
	}

	public ResultRow getCurrent() throws Exception {
		return currentResult;
	}

	public ResultSchema getSchema() throws Exception {
		return schema;
	}
	
	public void close() throws Exception {
		s.close();
	}

	private ResultSchema schema;
	private ResultSchema tSchema;
	private ResultRow currentResult;

	private ArrayList<Integer> order;
	private boolean aggr;
	private boolean aggr_get;

	private Scan s;
	private String db;
	private Transaction tx;
	private SelectExpList fieldList;
	/* (non-Javadoc)
	 * @see SQL.scan.Scan#hasField(java.lang.String)
	 */
	@Override
	public boolean hasField(String fldname) throws Exception {
		// TODO Auto-generated method stub
		return false;
	}
}
